﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CutAways.Geometry;

namespace CutAways.Illustration
{
	class Animation
	{
		private int currentFrame;
		private readonly int openingFrameCount, movingFrameCount;

		private Camera startViewpoint;
		private Camera endViewpoint;

		public float CutOpenFactor { get; private set; }
		public Camera CurrentViewpoint { get; private set; }

		public event EventHandler Closed;
		public event EventHandler Moved;
		public event EventHandler Finished;

		public bool IsFinished
		{
			get { return currentFrame >= (2 * openingFrameCount + movingFrameCount); }
		}

		public Animation(Camera startViewpoint, Camera endViewpoint, int startFrameCount, int openingFrameCount, int movingLengthUnitFrameCount)
		{
			this.startViewpoint = startViewpoint;
			this.endViewpoint = endViewpoint;

			this.CurrentViewpoint = this.startViewpoint.Clone();
			this.CutOpenFactor = 1.0f;

			this.currentFrame = startFrameCount;
			this.openingFrameCount = openingFrameCount;
			this.movingFrameCount = computeMovingFrameCount(movingLengthUnitFrameCount);

			//System.Windows.Forms.MessageBox.Show(startViewpoint.Center.ToString() + "; " + endViewpoint.Center, "centers");
		}

		public void NextFrame()
		{
			if (IsFinished)
			{
                if (Finished != null)
                {
                    CutOpenFactor = 1f;
                    Finished(this, EventArgs.Empty);
                }
				return;
			}

            if (currentFrame == openingFrameCount)
            {
                CutOpenFactor = 0f;
                if(Closed != null)
                    Closed(this, EventArgs.Empty);
            }
			if (currentFrame == openingFrameCount + movingFrameCount)
			{
				CurrentViewpoint = endViewpoint.Clone(); /**/ // pro jistotu
                if (Moved != null)
                    Moved(this, EventArgs.Empty);
			}

			if (currentFrame < openingFrameCount) // closing
				CutOpenFactor = 1.0f - ((float)currentFrame / (float)openingFrameCount);
			else if (currentFrame < openingFrameCount + movingFrameCount) // moving
				CurrentViewpoint = computeCurrentViewpoint();
			else
				CutOpenFactor = (float)(currentFrame - (openingFrameCount + movingFrameCount)) / (float)openingFrameCount;


			currentFrame++;
		}
		
		private int computeMovingFrameCount(int movingLengthUnitFrameCount)
		{
			//float angle = Vector.AngleBetween(startViewpoint.Eye - startViewpoint.Center, endViewpoint.Eye - startViewpoint.Center, Vector.Cross(startViewpoint.Eye - startViewpoint.Center, endViewpoint.Eye - startViewpoint.Center));
			//float ratio = Math.Abs(angle) / (float)Math.PI;

			return Math.Min((int)((endViewpoint.Eye - startViewpoint.Eye).GetLength() * (float)movingLengthUnitFrameCount), 5 * movingLengthUnitFrameCount);
		}
		
		private Camera computeCurrentViewpoint()
		{
			float progress = (float)(currentFrame - openingFrameCount) / (float)movingFrameCount;
			Vector axis = Vector.Norm(Vector.Cross(startViewpoint.Eye - startViewpoint.Center, endViewpoint.Eye - endViewpoint.Center));
			float angle = Vector.AngleBetween(startViewpoint.Eye - startViewpoint.Center, endViewpoint.Eye - startViewpoint.Center, axis);

			float rotateAngle = Vector.NonLinearTransitionBetween(0f, angle, progress);

			Vector eye, center, up;

			center = Vector.NonLinearTransitionBetween(startViewpoint.Center, endViewpoint.Center, progress);
			up = Vector.NonLinearTransitionBetween(startViewpoint.Up, endViewpoint.Up, progress);
			eye = Vector.Rotate(startViewpoint.Eye - startViewpoint.Center, rotateAngle, axis) + startViewpoint.Center;
			eye = Vector.SetLength(eye, Vector.NonLinearTransitionBetween(startViewpoint.Eye.GetLength(), endViewpoint.Eye.GetLength(), progress));

			return new Camera { Eye = eye, Center = center, Up = up };
		}
	}
}
